﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace pshcli
{

    //public enum platform
    //{
    //    managed,
    //    unmanaged
    //}


    //public enum size
    //{
    //    small,
    //    medium,
    //    large
    //}


    //public enum name
    //{
    //    BranchAndBound,
    //    CombinationalLogic,
    //    DenseAlgebra,
    //    DynamicProgramming,
    //    FiniteStateMachine,
    //    GraphModels,
    //    GraphTraversal,
    //    MapReduce,
    //    SparseAlgebra,
    //    SpectralMethod,
    //    StructuredGrid,
    //    UnstructuredGrid,
    //    NBody
    //}


    //public enum parallel
    //{
    //    serial,
    //    openmp,
    //    tpl,
    //    mpi,
    //    mpinet,
    //    hybrid
    //}


    public class Dwarf
    {

        public platform platform;
        public parallel parallel;
        public size size;
        public name internalname;

        public string dwarfname;
        public string bin;
        public string args;
        public string workdir;
         
        public string inputfile;
        public string resultfile;
        public string profilefile;
        public bool redirect;

        public bool xperftrace;
        public string xperftracefile;

        public bool mpitrace;
        public string mpitracefile;
        public string mpitracestr;


        public string inputfileswitch = "/inputfile:";
        public string resultfileswitch = "/resultfile:";
        public string profilefileswitch = "/profilefile:";

        public string mpiexecstr;

        public string scheduler;
        public string jobstr;
        public int numthreads;
        public int numprocs;

        public bool success;
        public string errormsg;


        #region helper methods
        public string resultdir;
        public string profiledir;
        #endregion


        public Dwarf(string dwarfname)
        {
            this.dwarfname = dwarfname;
        }


        public Dwarf
            (
            name name,
            platform platform,
            parallel parallel,
            size size,

            //string dwarfname,
            string bin,
            string args,
            string workdir,
            
            string inputfile,
            string resultfile,
            string profilefile,
            
            bool redirect,
            bool mpitrace,
            bool xperftrace,
            //string xperftracefile,
            //string mpitracefile,
            string mpiexecstr,
            string scheduler,
            string jobstr,
            int numthreads,
            int numprocs
            ) 
        {
            this.internalname = name;
            this.dwarfname = "Dwarf." + internalname.ToString();

            this.platform = platform;
            this.parallel = parallel;
            this.size = size;

            this.bin = bin;
            this.args = args;
            this.workdir = workdir;

            this.inputfile = inputfile;
            this.resultfile = resultfile;
            this.profilefile = profilefile;

            this.redirect = redirect;
            this.mpitrace = mpitrace;
            this.xperftrace = xperftrace;

            if(xperftrace)
                this.xperftracefile = ConstructXperfEtlFileName();

            if(mpitrace)
                this.mpitracefile = ConstructMpiEtlFileName();

            this.mpiexecstr = mpiexecstr;
            this.scheduler = scheduler;
            this.jobstr = jobstr;
            this.numthreads = numthreads;
            this.numprocs = numprocs;

            success = false;
        }


      
        //Replace sufix for tracing output
        public string ConstructXperfEtlFileName()
        {
            string str = this.profilefile.Replace(".txt", ".xperf.etl");
            //HelperMethods.CheckFile(str);
            return str;
        }
         
        public string ConstructMpiEtlFileName()
        {
            string str = this.profilefile.Replace(".txt", ".mpi.etl");
            //HelperMethods.CheckFile(str);
            return str;
        }

        public string ConstructExecCommand()
        {
            string cmd = "";

            //add mpiexec
            if (parallel == parallel.mpi || parallel == parallel.mpinet || parallel == parallel.hybrid)
            {
                cmd += ConstructMpiexecStr() + " "; 
            }

            //UNCOMMENT IF RUN IN PROCESS.Filename.Cmd
            ////add path\Dwarf.Executable.exe
            //cmd += bin + " ";
            
            //add inputfile
            cmd += inputfileswitch + this.inputfile + " ";

            //add result file
            cmd += resultfileswitch + this.resultfile + " ";

            //add profile file
            cmd += profilefileswitch + this.profilefile + " ";

            //add default args
            cmd += args + " ";
            
            //add pipe to file
            if(redirect)
                cmd += "> " + resultfile + ".redirect";

            return cmd;
        }

        public string ConstructMpiexecStr()
        {
            //string tmp = "mpiexec " + mpiexecstr;
            string tmp = mpiexecstr;
            if (this.mpitrace)
                tmp += " -trace " + mpitracestr + " -tracefile " + this.mpitracefile;

            tmp += " " + bin + " ";
                return tmp;

        }

        //Results section
        public Object _size;
        public double time;

        public void GetResult()
        {
            this.time = GetTiming(this.profilefile);
        }
        
        private double GetTiming(string profilefile)
        {

            //check output file
            HelperMethods.CheckFile(profilefile);

            TextReader txtreader = new StreamReader(profilefile);

            while (true)
            {
                string content = txtreader.ReadLine();
                if(content == null)
                    break;
                else if (content.Contains("time"))
                {
                    string message = "";
                    try
                    {
                        string[] strarr = content.Split(' ');
                        return Double.Parse(strarr[3]);
                    }
                    catch (Exception _e)
                    {
                        message = "Error parsing profile file: " + _e.Message;
                        throw new Exception(message, _e);
                    }
                }
            }
            Exception e = new Exception("Something went wrong reading the profile file (" + profilefile + ").");
            throw e;

        }


        //Consistency checks
        public bool CeckConsistency()
        {
            string error = "";
            if(!HelperMethods.IsFileReadable(this.bin, FileMode.Open, FileAccess.Read, FileShare.None))
                error += "Cannot read executable: " + this.bin + "\n";
                
            if(!HelperMethods.IsFileReadable(this.inputfile, FileMode.Open, FileAccess.Read, FileShare.None))
                error += "Cannot read input file: " + this.inputfile + "\n";

            
            //Check resultfile
            try
            {
                HelperMethods.IsFileReadable(this.resultfile, FileMode.CreateNew, FileAccess.Write, FileShare.None);
            }
            catch (Exception e)
            {
                if (e.Message.Contains("already exists"))
                {
                    if (!HelperMethods.DeleteFile(resultfile))
                        return false;
                }
                else
                    Console.WriteLine("Consistency check failed on dwarf {0}. IO error: {1}.", this.internalname, e.Message);
            }

            //Check profile file
            try
            {
                HelperMethods.IsFileReadable(this.profilefile, FileMode.CreateNew, FileAccess.Write, FileShare.None);
            }
            catch (Exception e)
            {
                if (e.Message.Contains("already exists"))
                {
                    if (!HelperMethods.DeleteFile(resultfile))
                        return false;
                }
                else
                    Console.WriteLine("Consistency check failed on dwarf {0}. IO error: {1}.", this.internalname, e.Message);
            }

            return true;
        }
    }
}
